#include "stdafx.h"
#include <boost/test/auto_unit_test.hpp>
#include <algorithm>
#include <boost/lambda/lambda.hpp>
#include "bmock.hpp"
#include "bmock_in_out_raw_mem.hpp"
#include "bmock_control_tools_mock.hpp"
#include <boost/test/unit_test.hpp>

struct obj
{
	int y;
};

static void f(const void* const buf, const size_t len) {
	STATIC_MOCK_VOID(2,(RAW_MEM(IN,buf,len),IN(len)));
}
static void g(void *const outbuf, const size_t len) {
	STATIC_MOCK_VOID(2,(RAW_MEM(OUT,outbuf,len),IN(len)));
}
static void h(void *const buf, const size_t len) {
	STATIC_MOCK_VOID(2,(RAW_MEM(IN_OUT,buf,len),IN(len)));
}
static void l(const obj& outbuf, const size_t len) {
	STATIC_MOCK_VOID(2,(RAW_MEM(OUT,outbuf,len),IN(len)));
}
static void l_1(const obj& outbuf, const size_t len) {
	STATIC_MOCK_VOID(2,(RAW_MEM(IN,outbuf,len),IN(len)));
}
static void l_2(const obj& outbuf, const size_t len) {
	STATIC_MOCK_VOID(2,(RAW_MEM(IN_OUT,outbuf,len),IN(len)));
}

static const char          s_inp_buf[]  = "000102030405060708090A0B0C0D0E0F";
static const unsigned char inp_buf[]    = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07
										  ,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F};
static const size_t        L1 = sizeof(inp_buf);
static const char          s_buf_name[] = "bmock::raw_mem(buf,len)";

static const char          s_out_buf[]  = "101112131415161718191A1B1C1D1E1F";
static const unsigned char out_buf[]    = {0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17
										  ,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F};
static const size_t        L2 = sizeof(out_buf);

static const size_t        DELTA = 10;
static const size_t        L3    = L2+DELTA;
static unsigned char       act_buf[L3];

static void set_up() {
	std::fill_n(act_buf,L3,0);
}

struct in_raw_buffer {};
BMOCK_TEST(in_raw_buffer,test__OK) {
	BMOCK_EXPECT(f(inp_buf,L1));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(f(inp_buf,L1));
	BMOCK_VERIFY;
}

struct out_raw_buffer {
	out_raw_buffer() {
		std::fill_n(act_buf,L3,0);
	}

	static const size_t  DELTA = 10;
	static const size_t  L3    = L2+DELTA;
	unsigned char        act_buf[L3];
};

const size_t  out_raw_buffer::DELTA;
const size_t  out_raw_buffer::L3;

BMOCK_TEST(out_raw_buffer,test_OK) {
	BMOCK_EXPECT(g(bmock::out_raw_mem(out_buf,L2),L3));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(g(act_buf,L3));
	BMOCK_VERIFY;
	BOOST_CHECK_EQUAL(L2,bmock::mismatch_at(out_buf,out_buf+L2,act_buf));
	BOOST_CHECK_EQUAL(DELTA,bmock::index_of(act_buf+L2,act_buf+L3,boost::lambda::_1!=0));
}

struct in_out_raw_buffer : public out_raw_buffer {
	in_out_raw_buffer()
		:in_out_buf(inp_buf,L1,out_buf,L2)
	{
		std::copy(inp_buf,inp_buf+L1,act_buf);
	}

	bmock::in_out_raw_mem_descr<unsigned char> in_out_buf;
};

BMOCK_TEST(in_out_raw_buffer,test_in_out_raw_buffer_OK) {
	BMOCK_EXPECT(h(in_out_buf,L1));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(h(act_buf,L1));
	BMOCK_VERIFY;
	BOOST_CHECK_EQUAL(L2,bmock::mismatch_at(out_buf,out_buf+L2,act_buf));
	BOOST_CHECK_EQUAL(DELTA,bmock::index_of(act_buf+L2,act_buf+L3,boost::lambda::_1!=0));
}

BOOST_AUTO_TEST_CASE(testUpdateArgument_OutRawBuf_InvalidLength) {
	__BMOCK_CONTROL(c);
	const bmock::control_tools_ptr t(new bmock::control_tools_mock(c));
	const char              name[] = "outbuf";
	const std::string            s_len = boost::lexical_cast<std::string>((unsigned int)(L2-1));
	const bmock::location          loc(__FILE__,__LINE__+10);

    __BMOCK_EXPECT_C(c,t->require_equal(16,16,bmock::message(bmock::IN_OUT_RAW_MEM_PAD_LEN,name),loc));
	__BMOCK_EXPECT_RETURN_C(c,false,t->check_equal(s_len,s_len,bmock::message(bmock::ARG_VALUE,"len"),loc));
	__BMOCK_EXPECT_RETURN_C(c,false,t->check_ptr(false,false,bmock::message(bmock::ARG_PTR,"outbuf"),loc));
	__BMOCK_EXPECT_RETURN_C(c,true,t->check_less_or_equal(L2,L2-1,bmock::message(bmock::RAW_MEM_LEN,name),loc));
	__BMOCK_EXPECT_THROW_C(c,new bmock::location(loc),t->abort(loc));
	__BMOCK_REPLAY_C(c);
	{
		bmock::test_mode_control c1(bmock::location(__FILE__,__LINE__),t);
		__BMOCK_EXPECT_C(c1,g(bmock::out_raw_mem(out_buf,L2),L2-1));
		__BMOCK_REPLAY_C(c1);
		set_up();
		BOOST_CHECK_THROW(g(act_buf,L2-1),bmock::location);
		BOOST_CHECK_EQUAL(L3,bmock::index_of(act_buf,act_buf+L3,boost::lambda::_1!=0));
		__BMOCK_VERIFY_C(c1);
	}
	__BMOCK_VERIFY_C(c);
}
struct YYY
{
	int i;
	char x;
	int *l;
};

static void k(const void* const buf, const size_t len) {
	STATIC_MOCK_VOID(2,(RAW_MEM(IN,buf,len),IN(len)));
}
static void k_2(YYY* buf_1, const size_t len_1) {
	STATIC_MOCK_VOID(2,(RAW_MEM(IN,buf_1,len_1),IN(len_1)));
}
static YYY buf_4 [3];

BMOCK_TEST(in_raw_buffer,test_RAW_IN_OK) {
	BMOCK_EXPECT(k(inp_buf,L1));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(k(inp_buf,L1));
	BMOCK_VERIFY;
}

BMOCK_TEST(in_raw_buffer,test_RAW_IN_not_void_OK) {
	buf_4[0].i=1;
	buf_4[0].x='1';
	buf_4[0].l=NULL;
	BMOCK_EXPECT(k_2(buf_4,3));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(k_2(buf_4,3));
	BMOCK_VERIFY;
}
struct reference_raw {
	obj a;
	obj b;
	obj c;
};
BMOCK_TEST(reference_raw,test_RAW_IN_reference)
{
	a.y=0;
	b.y=0;
	BMOCK_EXPECT(l_1(a,1));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(l_1(b,1));
	BMOCK_VERIFY;
}
BMOCK_TEST(reference_raw,test_RAW_OUT_reference)
{
	a.y=0;
	b.y=1;
	BMOCK_EXPECT(l(bmock::out_raw_mem(a),1));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(l(b,1));
	BMOCK_VERIFY;
	BOOST_CHECK_EQUAL(b.y,a.y);
}
BMOCK_TEST(reference_raw,test_RAW_IN_OUT_out_raw_mem_reference)
{
	a.y=0;
	b.y=1;
	BMOCK_EXPECT(l_2(bmock::out_raw_mem(a),1));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(l_2(b,1));
	BMOCK_VERIFY;
	BOOST_CHECK_EQUAL(b.y,a.y);
}
BMOCK_TEST(reference_raw,test_RAW_IN_OUT_in_raw_mem_reference)
{
	a.y=0;
	b.y=0;
	BMOCK_EXPECT(l_2(bmock::in_raw_mem(a),1));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(l_2(b,1));
	BMOCK_VERIFY;
	BOOST_CHECK_EQUAL(b.y,a.y);
}
BMOCK_TEST(reference_raw,test_RAW_IN_OUT_in_out_raw_mem_reference)
{
	a.y=0;
	b.y=1;
	c.y=0;
	BMOCK_EXPECT(l_2(bmock::in_out_raw_mem(a,b),1));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(l_2(c,1));
	BMOCK_VERIFY;
	BOOST_CHECK_EQUAL(b.y,c.y);
}